package model

import (
	"github.com/imroc/req"
	"github.com/labstack/gommon/log"
	"gopkg.in/mgo.v2/bson"
	"src/config"
	"src/util"
	"src/util/context"
)

// WeixinTokenRes 微信 access_token 获取返回数据结构体
type WeixinTokenRes struct {
	AccessToken  string `json:"access_token"`
	ExpiresIn    int    `json:"expires_in"`
	RefreshToken string `json:"refresh_token"`
	Openid       string `json:"openid"`
	Scope        string `json:"scope"`
	Errcode      int    `json:"errcode"`
	Errmsg       string `json:"errmsg"`
}

type WeixinWebTokenRes struct {
	AccessToken  string `json:"access_token"`
	ExpiresIn    int    `json:"expires_in"`
	RefreshToken string `json:"refresh_token"`
	Openid       string `json:"openid"`
	Unionid      string `json:"unionid"`
	Scope        string `json:"scope"`
	Errcode      int    `json:"errcode"`
	Errmsg       string `json:"errmsg"`
}

type AccessTokenRes struct {
	AccessToken string `json:"access_token"`
	ExpiresIn   int64  `json:"expires_in"`
	Errcode     int    `json:"errcode"`
	Errmsg      string `json:"errmsg"`
}

type WeixinWebUserInfo struct {
	Openid     string   `json:"openid" bson:"openid"`
	Unionid    string   `json:"unionid" bson:"unionid"`
	Nickname   string   `json:"nickname" bson:"nickname"`
	Sex        int      `json:"sex" bson:"sex"`
	Province   string   `json:"province" bson:"province"`
	City       string   `json:"city" bson:"city"`
	Country    string   `json:"country" bson:"country"`
	HeadImgURL string   `json:"headimgurl" bson:"headimgurl"`
	Privilege  []string `json:"privilege" bson:"privilege"`
	Errcode    int      `json:"errcode" bson:"errcode"`
	Errmsg     string   `json:"errmsg" bson:"errmsg"`
}

type WeixinSessRes struct {
	Unionid    string `json:"unionid"`
	Openid     string `json:"openid"`
	SessionKey string `json:"session_key"`
	Errcode    int    `json:"errcode"`
	Errmsg     string `json:"errmsg"`
}

type DbWeixinSession struct {
	ID         bson.ObjectId `bson:"_id,omitempty" json:"id,omitempty"`
	Openid     string        `json:"openid"`
	SessionKey string        `json:"session_key"`
}

type WeixinSession struct {
	Openid     string `json:"openid"`
	SessionKey string `json:"session_key"`
}

const (
	RedisKeyWeixinAccessToken = "access_token"
)

func init() {
	RefreshWeixinAccessToken()
}

// GetWeixinAccessToken 获得微信access_token
func RefreshWeixinAccessToken() {
	t, err := GetWeixinAccessToken()
	if err != nil {
		log.Errorf("Error on updating access_token from request: %s", err.Error())
		return
	}

	if t.Errcode != 0 {
		log.Errorf("Error on updating access_token from weixin: %s", t.Errmsg)
		return
	}
	RedisDBCntlr := NewRedisDBCntlr()
	_, err = RedisDBCntlr.SET(RedisKeyWeixinAccessToken, t.AccessToken)
	log.Printf("access_token has been refreshed")
}
func GetWeixinAccessToken() (AccessTokenRes, error) {
	ret := AccessTokenRes{}
	appInfo := config.Conf.Wechat
	param := req.Param{
		"appid":      appInfo.AppID,
		"secret":     appInfo.AppSecret,
		"grant_type": "client_credential",
	}
	url := config.WeixinGetTokenURI
	err := context.BindGetJSONData(url, param, &ret)
	return ret, err
}
func PostWeixinMsgSecCheck(text string) (bool, error) {
	RedisDBCntlr := NewRedisDBCntlr()
	AccessToken, err := RedisDBCntlr.GET(RedisKeyWeixinAccessToken)
	//t, err := GetWeixinAccessToken()
	if err != nil {
		return true, err
	}
	return util.PostWeixinMsgSecCheck(AccessToken, text)
}

func GetWeixinSession(code string) (WeixinSessRes, error) {
	data := WeixinSessRes{}
	appInfo := config.Conf.Wechat
	param := req.Param{
		"appid":      appInfo.AppID,
		"secret":     appInfo.AppSecret,
		"js_code":    code,
		"grant_type": "authorization_code",
	}
	url := config.WechatSessionURIPrefix
	err := context.BindGetJSONData(url, param, &data)
	return data, err
}
func DecryptWeixinEncryptedData(sessionKey, encryptedData, iv string) (*util.DecryptUserInfo, error) {
	appID := config.Conf.Wechat.AppID
	pc := util.NewWXBizDataCrypt(appID, sessionKey)
	return pc.Decrypt(encryptedData, iv)
}
func GetWeixinWebUserInfo(accessToken, openid string) (*util.DecryptUserInfo, error) {
	userInfo, err := getWeixinWebUserInfo(accessToken, openid)
	if err != nil {
		return nil, err
	}

	return &util.DecryptUserInfo{
		OpenID:    userInfo.Openid,
		UnionID:   userInfo.Unionid,
		NickName:  userInfo.Nickname,
		Gender:    userInfo.Sex,
		City:      userInfo.City,
		Province:  userInfo.Province,
		Country:   userInfo.Country,
		AvatarURL: userInfo.HeadImgURL,
	}, nil
}
func getWeixinWebUserInfo(accessToken, openid string) (WeixinWebUserInfo, error) {
	param := req.Param{
		"access_token": accessToken,
		"openid":       openid,
	}
	userInfo := WeixinWebUserInfo{}
	err := context.BindGetJSONData(config.WechatOpenUserInfo, param, &userInfo)
	if err != nil {
		return userInfo, err
	}
	return userInfo, nil
}

func SetWeixinSessionKey(openid, sessionkey string) error {
	query := bson.M{
		"openid": openid,
	}
	_, err := findSession(query, DefaultSelector)
	if err != nil {
		s := DbWeixinSession{
			Openid:     openid,
			SessionKey: sessionkey,
		}
		return insertSession(s)
	} else {
		update := bson.M{
			"$set": bson.M{
				"session_key": sessionkey,
			},
		}
		return updateSession(query, update)
	}
}

/******************************/
func updateSession(query, update interface{}) error {
	sess := globalSess.Clone()
	defer sess.Close()
	Table := sess.DB(DBNAME).C(SESSIONTABLE)

	return Table.Update(query, update)
}
func insertSession(docs ...interface{}) error {
	sess := globalSess.Clone()
	defer sess.Close()
	Table := sess.DB(DBNAME).C(SESSIONTABLE)

	return Table.Insert(docs...)
}
func findSession(query, selector interface{}) (WeixinSession, error) {
	sess := globalSess.Copy()
	defer sess.Close()
	Table := sess.DB(DBNAME).C(SESSIONTABLE)

	session := WeixinSession{}
	err := Table.Find(query).Select(selector).One(&session)
	return session, err
}
